package OliveFeed;

# Copyright (c) 2005, Shawn Boyette
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in
#   the documentation and/or other materials provided with the
#   distribution.
#  * Neither the name of the Olive Newsreader  nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

require Exporter;
use warnings;
use strict;
use OliveStory;
use OliveXML;

our @ISA       = qw(Exporter);
our @EXPORT    = qw( &feedpoll &forcefetch &getfeed &storefeed );

#######################################################################
##  feed handling logic  ****##########################################
#######################################################################

sub feedpoll {
    my $cui = shift;
    my $c = $cui->userdata->{c};
    my $w = $cui->userdata->{wins};
    my $t = time();
    my $i = 0;

    my $sel = $w->{news}{list}->get_active_id;
    $cui->userdata->{nlsel} = $sel;

    foreach my $nick (keys %{$c->{feeds}}) {
        my $ttl   = $c->{feeds}{$nick}{ttl};
        my $last  = $c->{feeds}{$nick}{last};
        my $delta = $t - $last;
        if ($delta > $ttl) {
            fetchfeed($cui,$nick);
            sourcefeed($cui,$nick);
            refreshlist($cui);
            shiftlist($cui);
            $i++;
        }
    }

    if ($i) {
        my @lt   = localtime($t);
        my $when = $lt[2] . ':' . $lt[1];
        my $s = ''; $s = 's' if ($i > 1);
        my $msg  = "$i feed$s updated at $when";
        $w->{news}{ftr1}->text($msg . ' ' x ($w->{dim}[0] - length($msg)));
        $w->{news}{ftr1}->draw;
    }
}

#-------------------------------------------------------------

sub fetchfeed {
    my ($cui,$nick) = @_;
    my $w  = $cui->userdata->{wins};
    my $c  = $cui->userdata->{c};
    my $ua = $cui->userdata->{ua};

    my $feed = $c->{feeds}{$nick}{feed};
    my $feedfile = $cui->userdata->{feeds} . "/$nick";

    my $msg = "Fetching ".$c->{feeds}{$nick}{disp}."...";
    $w->{news}{ftr1}->text($msg . ' ' x ($w->{dim}[0] - length($msg)));
    $w->{news}{ftr1}->draw;

    my $rc = $ua->get($feed, ':content_file' => $feedfile);
    if ($rc->is_success) {
        $c->{feeds}{$nick}{last} = time();
        $c->write;
    } else {
        my $error = $rc->status_line;
        $cui->dialog(-message => "There was a problem problem fetching ".$c->{feeds}{$nick}{disp}.":\n$error\n\nYou can try again by refrehing your feeds with 'r'.",
                     -title   => "Error");
    }

    $w->{news}{ftr1}->text(' ' x $w->{dim}[0]);
    $w->{news}{ftr1}->draw;
}

#-------------------------------------------------------------

sub forcefetch {
    my $cui = shift;
    my $feeds = $cui->userdata->{c}{feeds};

    my $i = 0;
    foreach my $nick (keys %{$feeds}) {
        if ($feeds->{$nick}{force}) {
            fetchfeed($cui,$nick);
            sourcefeed($cui,$nick);
                $i++;
        }
    }
    
    $cui->dialog("No feeds marked force-able.") unless $i;
}

#-------------------------------------------------------------

sub getfeed {
    my $cui = shift;
    my $feed = '';
    my $feedname = '';

    my $fw = $cui->add('feed', 'Window',
                         -border   => 1,
                         -bfg      => 'blue',
                         -title    => 'Add New Feed',
                         -height   => 11,
                         -width    => 60,
                         -centered => 1,
                        );
    $fw->{l1} = $fw->add(undef, 'Label',
                         -text => "Location:",
                         -x    => 1,
                         -y    => 1,
        );
    $fw->{l2} = $fw->add(undef, 'Label',
                         -text => "Nickname:",
                         -x    => 1,
                         -y    => 3,
        );
    $fw->{loc} = $fw->add('fwlo', 'TextEntry',
                          -x       => 11,
                          -y       => 1,
                          -width   => 46,
                          -reverse => 1,
                          -onchange  => sub { $feed = $fw->{loc}->get },
        );
    $fw->{nic} = $fw->add('fwnn', 'TextEntry',
                          -x         => 11,
                          -y         => 3,
                          -width     => 17,
                          -maxlength => 16,
                          -reverse   => 1,
                          -onchange  => sub { $feedname = $fw->{nic}->get },
        );
    $fw->{frc} = $fw->add('fwfr', 'Listbox',
                          -values => [ 1 ],
                          -labels => { 1  => 'Mark feed as force-able' },
                          -multi    => 1,
                          -height   => 2,
                          -width    => 27,
                          -y        => 5,
                          -x        => 11,
        );
    $fw->{okb} = $fw->add('fwok', 'Buttonbox',
                        -y       => -2,
                        -x       => 40,
                        -buttons => [ { -label   => '< OK >',
                                        -value   => 1,
                                        -onpress => sub { storefeed($cui,$feed,$feedname,$fw->{frc}->get) }
                                      },
                                      { -label   => '< Cancel >',
                                        -value   => 0,
                                        -onpress => sub { $fw->loose_focus;
                                                          $cui->delete('feed');
                                                          $cui->draw },
                                      },

                        ],
        );
    $fw->draw;
    $fw->{loc}->focus;
}

#-------------------------------------------------------------

sub prunefeed {
    my ($md5s,$d,$nick) = @_;
    my %delete = ();

    my $statement = "SELECT id,md5 FROM stories WHERE nick = '$nick'";
    my $sth = $d->prepare($statement);
    $sth->execute;

    while (my $q = $sth->fetchrow_arrayref) {
        my $keep = 0;
        foreach my $md5 (keys %{$md5s}) {
            $keep = 1 if ($md5 eq $q->[1]);
        }
        $delete{$q->[0]} = 1 unless $keep;
    }

    foreach my $id (keys %delete) {
        $statement = "DELETE FROM stories WHERE id = $id";
        $sth = $d->prepare($statement);
        $sth->execute;        
    }
}

#-------------------------------------------------------------

sub sourcefeed {
    my ($cui,$nick) = @_;
    my $c  = $cui->userdata->{c};
    my $fd = $cui->userdata->{feeds};
    my $w  = $cui->userdata->{wins};
    my $x  = $cui->userdata->{xml};
    
    my $msg = "Parsing ".$c->{feeds}{$nick}{disp}."...";
    $w->{news}{ftr1}->text($msg . ' ' x ($w->{dim}[0] - length($msg)));
    $w->{news}{ftr1}->draw;

    my $f = $x->XMLin("$fd/$nick");
    my %data = ();
    my $md5s = 0;
    
    if ($f->{version}) { # feed is RSS(?) ##################################################
        $data{title} = $f->{channel}{title};
        $data{ttl}   = 3600;
        $data{ttl}   = $f->{channel}{ttl} * 60 if ($f->{channel}{ttl});
        # iterate over the feed items, get back a list of md5sums
        $md5s = parseitems($f->{channel}{item},$cui,$nick);
    } elsif ($f->{channel}{'dc:date'} or $f->{channel}{'rdf:about'}) { # feed is RDF 0.9/1.0
        $data{title} = $f->{channel}{title};

        # hash out TTL
        $data{ttl}   = 86400;
        if ($f->{channel}{'syn:updatePeriod'}) {
            my $freq = $f->{channel}{'syn:updatePeriod'};
            $data{ttl} = 3600           if ($freq eq 'hourly');
            $data{ttl} = 86400          if ($freq eq 'daily');
            $data{ttl} = 86400 * 7      if ($freq eq 'weekly');
            $data{ttl} = 86400 * 7 * 30 if ($freq eq 'monthly');
        }
        if ($f->{channel}{'syn:updateFrequency'}) {
            $data{ttl} = int( $data{ttl} / $f->{channel}{'syn:updateFrequency'} );
        }
        # iterate over the feed items, get back a list of md5sums
        $md5s = parseitems($f->{item},$cui,$nick);
    } else {
        $cui->dialog(-title => "Problem",
                     -message => "Sorry, Olive doesn't recognize this feed's format.\n"
                                  . "You'll want to yank this feed from the list.");
    }

    # yank expired stories from the db
    $msg = "Pruning expired stories from ".$c->{feeds}{$nick}{disp}."...";
    $w->{news}{ftr1}->text($msg . ' ' x ($w->{dim}[0] - length($msg)));
    $w->{news}{ftr1}->draw;
    prunefeed($md5s,$cui->userdata->{dbh},$nick);
    
    $c->{feeds}{$nick}{ttl} = $data{ttl};
    $c->{feeds}{$nick}{title} = $data{title};
    $c->write;
    
    $w->{news}{ftr1}->text(' ' x $w->{dim}[0]);
    $w->{news}{ftr1}->draw;
}

#-------------------------------------------------------------

sub storefeed {
    my ($cui,$feed,$nick,$force) = @_;
    my $c  = $cui->userdata->{c};
    my $ua = $cui->userdata->{ua};

    # check for null data
    if ($feed eq '' or $nick eq '') {
        $cui->dialog(-message => "Location and Nickname must both have values\nfor a new feed to be added.",
                     -title   => "Error");
        return 1;
    }

    # trim whitespace
    $feed =~ s/^\s+//;
    $feed =~ s/\s+$//;
    $nick =~ s/^\s+//;
    $nick =~ s/\s+$//;

    # save original nick for display and sanitize
    my $disp = $nick;
    $nick =~ s/\s/_/g;
    $nick =~ s/\W//g;
    $nick = lc($nick);

    # check for dupe nicks
    if (defined $c->{feeds}{$nick}) {
        $cui->dialog(-message => "There is already a feed with that nick.",
                     -title   => "Error");
        return 1;
    }

    # check for dupe feeds
    foreach my $f (keys %{$c->{feeds}}) {
        if ($feed eq $c->{feeds}{$f}{feed}) {
            $cui->dialog(-message => "There is already a nick with that URI: $f",
                         -title   => "Error");
            return 1;
        }
    }

    # poke it over the network to see if it exists
    my $rc = $ua->head($feed);
    unless ($rc->is_success) {
        my $error = $rc->status_line;
        $cui->dialog(-message => "There was a network problem:\n$error",
                     -title   => "Error");
        return 1;
    }

    # everything looks okay. store it.
    $force = 0 unless (defined $force);
    $c->{feeds}{$nick}{feed}  = $feed;
    $c->{feeds}{$nick}{disp}  = $disp;
    $c->{feeds}{$nick}{force} = $force;
    $c->{feeds}{$nick}{last}  = 0;
    $c->{feeds}{$nick}{ttl}   = 0;
    $c->write;

    # kill our parent window
    $cui->delete('feed');
    $cui->draw;
    
    # fetch and process the new feed
    fetchfeed($cui,$nick);
    sourcefeed($cui,$nick);
    refreshlist($cui);

    return 0;
}

1;
